﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Geometry
{
    public class GObjectGenerator : INotifyPropertyChanged
    {
        protected MethodInfo Generator { get; private set; }
        public GObjectGeneratorAttribute Attrib { get; private set; }

        public IGObject Generate()
        {
            Debug.Assert(this.IsApplicableToSelection);
            return this.Generator.Invoke(null, m_SelectionAsArgs) as IGObject;
        }

        public string Requirements { get; private set; }

        private bool m_IsApplicableToSelection;
        public bool IsApplicableToSelection
        {
            get { return m_IsApplicableToSelection; }
            private set
            {
                if (m_IsApplicableToSelection == value) return;
                m_IsApplicableToSelection = value;
                PropertyChangedEventHandler temp = PropertyChanged;
                if (temp != null)
                {
                    temp(this, new PropertyChangedEventArgs("IsApplicableToSelection"));
                }
            }
        }

        internal void UpdateToSelection(List<IGObject> selected)
        {
            this.IsApplicableToSelection = CanApplyGeneratorTo(selected);
            if (!this.IsApplicableToSelection)
            {
                m_SelectionAsArgs = null;
            }
        }

        private IGObject[] m_SelectionAsArgs;

        private bool CanApplyGeneratorTo(List<IGObject> selected)
        {
            var prms = this.Generator.GetParameters();
            if (prms.Length > 0 && selected.Count != prms.Length) return false;
            var unused = selected.ToList();
            var args = new List<IGObject>();
            foreach (var prm in prms)
            {
                var go = (from item in unused
                          where prm.ParameterType.IsAssignableFrom(item.GetType())
                          select item).FirstOrDefault();
                if (go == null) return false;
                args.Add(go);
                unused.Remove(go);
            }
            m_SelectionAsArgs = args.ToArray();
            return true;
        }

        public GObjectGenerator(MethodInfo _generator)
        {
            this.Generator = _generator;
            this.Attrib = this.Generator.GetCustomAttributes(typeof(GObjectGeneratorAttribute), false)[0] as GObjectGeneratorAttribute;
            this.IsApplicableToSelection = false;

            // TODO: ugly code, transfer to UI
            var req = this.Generator.GetParameters().ToLookup(prm => prm.ParameterType);
            if (req.Count > 0)
            {
                var sb = new StringBuilder();
                sb.Append("This generator requires");
                foreach (var type in req)
                {
                    sb.Append(" ").Append(type.Count()).Append(" ").Append(ConvertToUIName(type.Key)).Append("(s)");
                }
                sb.Append(" to be selected.");
                this.Requirements = sb.ToString();
            }
            else
            {
                this.Requirements = "There are no requirements.";
            }
        }

        private string ConvertToUIName(Type type)
        {
            if (typeof(IGPoint).Equals(type)) return "point";
            else if (typeof(IGLine).Equals(type)) return "line";
            else if (typeof(IGCircle).Equals(type)) return "circle";
            else return "unknown";
        }



        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

}